﻿/* Copyright 2007-2008 dnAnalytics Project.
 *
 * Contributors to this file:
 * Jurgen Van Gael
 * Marcus Cuda
 *
 * This file is part of dnAnalytics.  dnAnalytics is licensed under the 
 * Microsoft Public License. See License.txt for a complete copy of the
 * license.
 */

using System;
using System.Security.Cryptography;

namespace dnAnalytics.Random
{
    /// <summary>
    /// A random number generator based on the <see cref="System.Security.Cryptography.RandomNumberGenerator"/> class in the .NET library.
    /// </summary>
    public class SystemCryptoRandomNumberGenerator : AbstractRandomNumberGenerator
    {
        private const double mReciprocal = 1.0 / uint.MaxValue;
        private readonly RandomNumberGenerator mRandom;

        /// <summary>
        /// Construct a new random number generator with a random seed.
        /// </summary>
        /// <remarks>Uses <see cref="System.Security.Cryptography.RNGCryptoServiceProvider"/> and uses the value of 
        /// <see cref="Control.ThreadSafeRandomNumberGenerators"/> to set whether the instance is thread safe.</remarks>
        public SystemCryptoRandomNumberGenerator(): this(new RNGCryptoServiceProvider(), Control.ThreadSafeRandomNumberGenerators)
        {
            mRandom = new RNGCryptoServiceProvider();
        }

        /// <summary>
        /// Construct a new random number generator with random seed.
        /// </summary>
        /// <param name="rng">The <see cref="RandomNumberGenerator"/> to use.</param>
        /// <remarks>Uses the value of  <see cref="Control.ThreadSafeRandomNumberGenerators"/> to set whether the instance is thread safe.</remarks>
        public SystemCryptoRandomNumberGenerator(RandomNumberGenerator rng) : this(rng, Control.ThreadSafeRandomNumberGenerators)
        {
        }

        /// <summary>
        /// Construct a new random number generator with random seed.
        /// </summary>
        /// <remarks>Uses <see cref="System.Security.Cryptography.RNGCryptoServiceProvider"/></remarks>
        /// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
        public SystemCryptoRandomNumberGenerator(bool threadSafe): this(new RNGCryptoServiceProvider(), threadSafe)
        {
        }

        /// <summary>
        /// Construct a new random number generator with random seed.
        /// </summary>
        /// <param name="rng">The <see cref="RandomNumberGenerator"/> to use.</param>
        /// <param name="threadSafe">if set to <c>true</c> , the class is thread safe.</param>
        public SystemCryptoRandomNumberGenerator(RandomNumberGenerator rng, bool threadSafe) : base(threadSafe)
        {
            if (rng == null)
            {
                throw new ArgumentNullException("rng");
            }
            mRandom = rng;
        }


        /// <summary>
        /// Returns a random number between 0.0 and 1.0.
        /// </summary>
        /// <returns>
        /// A double-precision floating point number greater than or equal to 0.0, and less than 1.0.
        /// </returns>
        protected override double DoSample()
        {
            byte[] bytes = new byte[4];
            mRandom.GetBytes(bytes);
            return BitConverter.ToUInt32(bytes, 0) * mReciprocal;
        }
    }
}